#ifndef MATRIX_HH_
#   define MATRIX_HH_

#	include "tools/Vector.hh"

// TODO: overload scale, rotate, translate, only 1 arg: a float: same value 4 each axe

/** 4 * 4 Matrix class
 ** with double brackets overloading
 */
class Matrix
{
    public:
        // Ctors - Dtors
        /** Default Constructor */ //  should not affect coords
        Matrix();

        /** Constructor: set all coords to 0 if \param identity is false
         ** sets Matrix to Identity otherwise
		 */
        Matrix(bool identity);

        /** Allow to set values in constructor;
         ** fill nb_elts with user's values, complete with 0
         */
        Matrix(float* values, int nb_elts);

        /** Allow to initialize a Matrix with 4 Vectors
         ** Warning: Matrix are column major
         ** => meaning that the 3 Vectors are the 3 first columns of the new Matrix
         ** then, Matrix is fill with 0s (last column && last row)
         */
        Matrix(Vector u, Vector v, Vector w);

        /** Copy Constructor */
        Matrix(Matrix const &m);

        /** Default destructor */
        virtual ~Matrix();
        // end Ctors - Dtor


        // Getters - Setters
        /** Access _values
         * \return The current value of _values
         */
        float* getValues();

        /** Set _values
         * \param val New value to set
         */
        void setValues(unsigned int i, unsigned int j, float val);
        // end Getters - Setters


        // Operators overloading
        Matrix operator* (Matrix const &m);
        Matrix& operator= (Matrix const &m);

		// double brackets overloading
		class MatrixRow
		{
			public:
        		// Ctor
	        	MatrixRow(Matrix& p, int matrix_i) : parent(p), i(matrix_i) {}
		  		float& operator[](int j) const { return parent.get(i, j); } // getting value // DONE check

			private:
	        	Matrix& parent;
				int i;
		};
		// Return an object that defines its own operator[] to access the data
		MatrixRow operator[](int i) { return MatrixRow(*this, i); }

        // stack
		bool stackPush();
        bool stackPop();
        void stackClear();

        void loadIdentity();

        // transformations
        /** translating current Matrix */
        void translate(Vector v);

        /** scaling current Matrix */
        void scale(Vector v);

        /** rotating current Matrix */
        void rotate(float angle, Vector v);
        // end transformations

		#ifdef DEBUG
            void print();
			#ifdef DEBUG_MATRIX
				void testValues();
				void testProduct();
			#endif // DEBUG_MATRIX
        #endif


    private:
		friend class MatrixRow;
		float& get(unsigned int i, unsigned int j);

		float _values[4][4];
        Matrix* _save;
};

#endif // MATRIX_HH_
